package Classification.LocalOutlierFactor;

import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.applet.*;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;

import Classification.kNN;
import DataMining.NumericDataSets;
import BasicMath.EuclideanDistance;

public class LocalOutlierFactor extends Applet {
	private static final long serialVersionUID = -3870974276502542973L;
	private static Double[][] DistanceMatrix;
	public static final int k = 2;
	
	private static final int width = 50;
	private static final int height = 50;
	private static final int scalingFactor = 8;
	
	private static Set<Outlier> list; 
	
	/**
	 * @param args
	 * @throws Exception 
	 */
	public static void main(String[] args) {
		Double[][] values = (Double[][]) NumericDataSets.BasicTestArray;
		
		try {
			list = new HashSet<Outlier>();
		} catch (Exception e) {}
		
		for (int i = 0; i < values.length; i++)
		{
			Outlier current = new Outlier(k, i, values[i]);
			Double factor = OutlierFactor(current, values);
			System.out.println(factor);
			list.add(current);
		}
		System.out.println(list.size());
		
		//Start Drawing
		LocalOutlierFactor myClass = new LocalOutlierFactor();
		JFrame frame = new JFrame();
		frame.setSize(new Dimension((int)(width*scalingFactor*1.05), (int)(height*scalingFactor*1.11)));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(myClass);
        frame.setVisible(true);
	}
	
	public void paint(Graphics g) {
	    for (int x=0;x<=width*scalingFactor; x+=10*scalingFactor)
	        g.drawLine(x,0,x,width*scalingFactor);
	    for (int y=0;y<=height*scalingFactor;y+=10*scalingFactor)
	        g.drawLine(0,y,height*scalingFactor,y);
	    
	    for (Double[] locations : NumericDataSets.ReturnAllArrays())
	    {
	    	int x = (int) Math.round(locations[0]*scalingFactor);
	    	int y = (int) Math.round(locations[1]*scalingFactor);
	    	g.fillOval(x-4, height*scalingFactor-y, 4, 4);
	    }
	    for (Outlier value : list)
	    {
	    	int x = (int) Math.round(value.Coordinates[0]*scalingFactor);
	    	int y = (int) Math.round(value.Coordinates[1]*scalingFactor);
	    	int radius = (int) Math.round(value.LocalOutlierFactor*scalingFactor);
	    	g.drawOval(x-4-(int)radius/2, height*scalingFactor-y-(int)radius/2, radius, radius);
	    }
	  }
	
	public static Double OutlierFactor(Outlier point, Double[][] DataSets)
	{
		try
		{
			BuildDistanceMatrix(DataSets);
		}
		catch (Exception e){}
		
		kNN compute = new kNN();
		point.KDistance = kNN.KDistance(point.k(), point.PositionInList(), DistanceMatrix);
		point.KDistanceNeighborhood = compute.GetKDistanceNeighborhood(point.k(), 
				point.PositionInList(), DistanceMatrix);
		
		point.LocalReachabilityDensity = LocalReachabilityDensity(point.k(), 
				point.KDistanceNeighborhood);
		
		point.LocalOutlierFactor = 0.0;
		Enumeration<Integer> keys = point.KDistanceNeighborhood.keys();
		for (int i = 0; i < point.KDistanceNeighborhood.size(); i++)
		{
			int key = keys.nextElement();
			
			Dictionary<Integer, Double> KDistanceNeighborhoodO = 
					compute.GetKDistanceNeighborhood(point.k(), key, DistanceMatrix);
			Double LocalReachabilityDensityO = LocalReachabilityDensity(point.k(), 
					KDistanceNeighborhoodO);
			
			point.LocalOutlierFactor += LocalReachabilityDensityO/point.LocalReachabilityDensity;
		}
		
		point.LocalOutlierFactor = point.LocalOutlierFactor
				/(double)point.KDistanceNeighborhood.size();
		
		return point.LocalOutlierFactor;
	}
	private static void BuildDistanceMatrix(Double[][] objects) throws Exception
	{
		DistanceMatrix = new Double[objects.length][objects.length]; 
		
		int size = objects.length;
		for (int i = 0; i < size; i++)
			for (int j= 0; j < size; j++)
				DistanceMatrix[i][j] = 0.0;
		
		for (int i = 0; i < size; i++)
		{
			for (int j = i+1; j < size; j++)
			{
				if (DistanceMatrix[i][j]==0)
				{
					DistanceMatrix[i][j] = EuclideanDistance.GetDistance(objects[i], objects[j]);
					DistanceMatrix[j][i] = DistanceMatrix[i][j];
				}
			}
		}
	}
	private static Double ReachabilityDistance(int k, Double pqDistance, Integer ItemPosition)
	{
		return Math.max(pqDistance, kNN.KDistance(k, ItemPosition, DistanceMatrix));
	}
	private static Double LocalReachabilityDensity(int k, Dictionary<Integer, Double> KDistanceNeighborhood)
	{
		Enumeration<Integer> keys = KDistanceNeighborhood.keys();
		Double LocalReachabilityDensity = 0.0;
		for(int i = 0; i < KDistanceNeighborhood.size(); i++)
		{
			Integer key = keys.nextElement();
			LocalReachabilityDensity += ReachabilityDistance(k, KDistanceNeighborhood.get(key), key);
		}
		LocalReachabilityDensity = (double)KDistanceNeighborhood.size()/LocalReachabilityDensity;
		
		return LocalReachabilityDensity;
	}
}
